/////////////////////////////////////////////     File     //////////////////////////////////////////////////
// Author: James Chen
// Filename: Camera.cpp
// Created: Saturday, August 14, 2010 9:56:02 AM
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
#include "./Camera.h"

#define SPEEDBOOST 150

////////////////////// Singleton Implementation Begins //////////////////////
CCamera *CCamera::m_pInstance = 0;

CCamera::CCamera(void)
{
	this->mTheta = 0.0f;
	this->mPhi = float(M_PI) * 0.25f;
	this->mRadius = 10.0f;

	this->m_vecTarget = D3DXVECTOR3(0.0f, 0.0f, 0.0f);

	this->ZoomScale = 1.0f;
	this->ZAxisLocation = 0;

	this->m_vecPosition = D3DXVECTOR3(0.0f, 10.0f, 50.0f);

	this->m_vecMousePosition = D3DXVECTOR3(0.0f, 0.0f, 0.0f);
	this->m_vecLastMousePosition = D3DXVECTOR3(0.0f, 0.0f, 0.0f);

}

CCamera::~CCamera(void)
{
}

CCamera *CCamera::GetInstance(void)
{
	if(!m_pInstance)
	{
		m_pInstance = new CCamera;
	}

	return m_pInstance;
}

void CCamera::DeleteInstance(void)
{
	if(m_pInstance)
	{
		delete m_pInstance;
		m_pInstance = 0;
	}
}
/////////////////////// Singleton Implementation Ends ///////////////////////

void CCamera::CameraMovement(float deltaTime)
{
	Input = CDXInput::GetInstance();

	m_vecMousePosition.x = Input->GetMouseAxis(XAXIS);
	m_vecMousePosition.y = Input->GetMouseAxis(YAXIS);
	m_vecMousePosition.z = 0;

	D3DXVECTOR3 LOOKAT;

	m_vecDeltaMousePosition = this->m_vecMousePosition - this->m_vecLastMousePosition;

	ZAxisLocationOld = ZAxisLocation;
	ZAxisLocation -= Input->GetMouseAxis(ZAXIS);

	AutoSetScale();

	

	if(Input->GetMouseButton(M_BUTTON_RIGHT))
	{
		if(Input->GetKey(DIK_LCONTROL) || Input->GetKey(DIK_RCONTROL))
		{
			//this->m_vecTarget += this->m_vecRight * deltaTime * SPEEDBOOST;
			this->m_vecPosition += D3DXVECTOR3(this->m_matView._11, 0, -this->m_matView._13) * deltaTime * SPEEDBOOST;
		}

		else if(false)
		{
			
		}

		else
		{
			D3DXVec3Cross(&LOOKAT, &D3DXVECTOR3(0.0f, 1.0f, 0.0f), &this->m_vecRight);
			D3DXVec3Normalize(&LOOKAT, &LOOKAT);

			this->m_vecTarget += (this->m_vecRight * deltaTime * SPEEDBOOST) * (-m_vecDeltaMousePosition.x) * this->ZoomScale;
			this->m_vecPosition += (this->m_vecRight * deltaTime * SPEEDBOOST) * (-m_vecDeltaMousePosition.x) * this->ZoomScale;

			this->m_vecTarget += (LOOKAT * deltaTime * SPEEDBOOST) * (-m_vecDeltaMousePosition.y) * this->ZoomScale;
			this->m_vecPosition += (LOOKAT * deltaTime * SPEEDBOOST) * (-m_vecDeltaMousePosition.y) * this->ZoomScale;
		}
	}

	//Store the last position of the mouse and look at the change between last and current position to see what we need to change

	if(Input->GetKey(DIK_RIGHT))
	{
		//this->m_vecTarget += D3DXVECTOR3(this->m_matView._11, this->m_matView._12, -this->m_matView._13);
		this->m_vecPosition += D3DXVECTOR3(this->m_matView._11, 0, -this->m_matView._13);
	}

	if(Input->GetKey(DIK_LEFT))
	{
		//this->m_vecTarget -= D3DXVECTOR3(this->m_matView._11, this->m_matView._12, -this->m_matView._13);
		this->m_vecPosition -= D3DXVECTOR3(this->m_matView._11, 0, -this->m_matView._13);
	}

	if(Input->GetKey(DIK_UP))
	{
		D3DXVec3Cross(&LOOKAT, &D3DXVECTOR3(0.0f, 1.0f, 0.0f), &this->m_vecRight);
		D3DXVec3Normalize(&LOOKAT, &LOOKAT);

		this->m_vecTarget -= (LOOKAT * deltaTime * SPEEDBOOST) * this->ZoomScale;
		this->m_vecPosition -= (LOOKAT * deltaTime * SPEEDBOOST) * this->ZoomScale;
	}

	if(Input->GetKey(DIK_DOWN))
	{
		D3DXVec3Cross(&LOOKAT, &D3DXVECTOR3(0.0f, 1.0f, 0.0f), &this->m_vecRight);
		D3DXVec3Normalize(&LOOKAT, &LOOKAT);

		this->m_vecTarget += (LOOKAT * deltaTime * SPEEDBOOST) * this->ZoomScale;
		this->m_vecPosition += (LOOKAT * deltaTime * SPEEDBOOST) * this->ZoomScale;
	}

#if 1

	// Update angles based on input to orbit camera around scene.
	if(GetAsyncKeyState('A') & 0x8000)	mTheta -= 1.0f*deltaTime;
	if(GetAsyncKeyState('D') & 0x8000)	mTheta += 1.0f*deltaTime;
	if(GetAsyncKeyState('W') & 0x8000)	mPhi -= 2.0f*deltaTime;
	if(GetAsyncKeyState('S') & 0x8000)	mPhi += 2.0f*deltaTime;
	if(GetAsyncKeyState('Z') & 0x8000)	mRadius -= 25.0f*deltaTime;
	if(GetAsyncKeyState('X') & 0x8000)	mRadius += 25.0f*deltaTime;

	mRadius = this->ZoomScale * 30;

	// Restrict the angle mPhi and radius mRadius.
	if( mPhi < 0.1f )	mPhi = 0.1f;
	if( mPhi > M_PI-0.1f)	mPhi = float(M_PI)-0.1f;

	if( mRadius < 25.0f) mRadius = 25.0f;

#endif

	this->m_vecLastMousePosition = this->m_vecMousePosition;
}

D3DXMATRIX CCamera::Update(float deltaTime)
{
	// Convert Spherical to Cartesian coordinates: mPhi measured from +y
	// and mTheta measured counterclockwise from -z.
	float x =  mRadius*sinf(mPhi)*sinf(mTheta);
	float z = -mRadius*sinf(mPhi)*cosf(mTheta);
	float y =  mRadius*cosf(mPhi);

	// Build the view matrix.
	//this->m_vecPosition.x = x;
	//this->m_vecPosition.y = y;
	//this->m_vecPosition.z = z;

	D3DXVECTOR3 tempPos;

	tempPos.x = this->m_vecPosition.x + x;
	tempPos.y = this->m_vecPosition.y + y;
	tempPos.z = this->m_vecPosition.z + z;

	D3DXVECTOR3 up(0.0f, 1.0f, 0.0f);
	D3DXMatrixLookAtLH(&m_matView, &tempPos, &this->m_vecTarget, &up);

	GetRightVector();
	GetUpVector();
	GetLookAtVector();

	D3DXVec3Normalize(&this->m_vecRight, &this->m_vecRight);
	D3DXVec3Normalize(&this->m_vecUp, &this->m_vecUp);
	D3DXVec3Normalize(&this->m_vecLookAt, &this->m_vecLookAt);

	return this->m_matView;
}

void CCamera::AutoSetScale()
{
	this->ZoomScale = 1 + ((this->ZAxisLocation / MOUSEWHEELCLICKSPEED) * ZOOMINCREMENT);

	if(this->ZoomScale < MINZOOM)
	{
		this->ZoomScale = MINZOOM;
		this->ZAxisLocation = ((ZoomScale - 1) / ZOOMINCREMENT) * MOUSEWHEELCLICKSPEED;
	}

	else if(this->ZoomScale > MAXZOOM)
	{
		this->ZoomScale = MAXZOOM;
		this->ZAxisLocation = ((ZoomScale - 1) / ZOOMINCREMENT) * MOUSEWHEELCLICKSPEED;
	}
}

D3DXVECTOR3 CCamera::GetRightVector()
{
	this->m_vecRight.x = this->m_matView(0, 0);
	this->m_vecRight.y = this->m_matView(0, 1);
	this->m_vecRight.z = this->m_matView(0, 2);

	return this->m_vecRight;
}

D3DXVECTOR3 CCamera::GetUpVector()
{
	this->m_vecUp.x = this->m_matView(1, 0);
	this->m_vecUp.y = this->m_matView(1, 1);
	this->m_vecUp.z = this->m_matView(1, 2);

	return this->m_vecUp;
}

D3DXVECTOR3 CCamera::GetLookAtVector()
{
	this->m_vecLookAt.x = this->m_matView(2, 0);
	this->m_vecLookAt.y = this->m_matView(2, 1);
	this->m_vecLookAt.z = this->m_matView(2, 2);

	return this->m_vecLookAt;
}